农产品电子商务的快速发展产生了海量交易数据,通过可视化分析可以:
EDA 由统计学家 John Tukey 于 1977 年提出,核心思想是在正式建模前理解数据特征:
| 原则 | 说明 |
|---|---|
| 怀疑精神 | 对数据和假设保持质疑 |
| 可视化优先 | 图形比数字更直观 |
| 计算支撑 | 用统计量验证视觉发现 |
| 迭代探索 | 分析是循环往复的过程 |
农产品数据与一般商品数据相比,具有四大显著特征:
以下代码与教学平台任务要求完全一致:
# 注:processed_data.csv数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
# 导入必要的数据分析和可视化库
import pandas as pd
import numpy as np # 导入NumPy数值计算库
import matplotlib.pyplot as plt # 导入Matplotlib绑图库
import seaborn as sns # 导入Seaborn可视化库
import warnings # 导入warnings模块用于控制警告输出
warnings.filterwarnings('ignore') # 忽略运行时警告信息以保持输出整洁
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体作为默认字体以支持中文
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
# 读取处理好的数据集
data = pd.read_csv('processed_data.csv')
# 查看数据集的基本情况
print(data.head()) # 显示数据集前几行
print("查看数据集的基本信息:") # 输出查看数据集的基本信息
print(data.info()) # 显示数据集的基本信息,包括列名、数据类型和非空值数量
print("查看数据集有无缺失值:") # 输出查看数据集有无缺失值
print(data.isnull().sum()) # 统计每列的缺失值数量
print("查看数据集有无重复值:") # 输出查看数据集有无重复值
print(data.duplicated().sum()) # 统计重复行的数量
# 创建特征映射字典,将英文列名映射为中文名称,用于图表标题
feature_map = {
'price': '价格', # 映射"price"→"价格"
'quantity': '数量', # 映射"quantity"→"数量"
'customer_age': '顾客年龄', # 映射"customer_age"→"顾客年龄"
'return_flag': '是否返单', # 映射"return_flag"→"是否返单"
'sales_amount': '销售额' # 映射"sales_amount"→"销售额"
} # 数据结构定义结束
# 创建箱型图展示各特征的分布情况
plt.figure(figsize=(20,10)) # 设置图形大小
for i, feature in enumerate(feature_map.keys()): # 循环遍历
plt.subplot(2,3, i+1) # 创建2行3列的子图布局
plt.title(f'{feature_map[feature]}的箱型图') # 设置子图标题
plt.boxplot(data[feature]) # 绑制箱线图
plt.xlabel(f'{feature_map[feature]}') # 设置x轴标签
plt.ylabel('频率') # 设置y轴标签
plt.savefig("特征箱型图.png") # 保存图形
# 创建一个字典来映射产品名称到正确的分类
category_mapping = {
'安溪铁观音': '茶叶', # 映射"安溪铁观音"→"茶叶"
'武夷岩茶': '茶叶', # 映射"武夷岩茶"→"茶叶"
'福州茉莉花': '茶叶', # 映射"福州茉莉花"→"茶叶"
'古田银耳': '食用菌', # 映射"古田银耳"→"食用菌"
'建宁莲子': '中药材', # 映射"建宁莲子"→"中药材"
'琯溪蜜柚': '水果', # 映射"琯溪蜜柚"→"水果"
'宁德大黄鱼': '水产品' # 映射"宁德大黄鱼"→"水产品"
} # 数据结构定义结束
# 应用映射来更新category列,将产品名称转换为产品类别
data['category'] = data['product_name'].map(category_mapping).fillna(data['category'])
# 2.可视化展示 - 计算并转置描述性统计数据(结果未打印,仅计算)
data.describe().T
# 2.1 顾客信息可视化
plt.figure(figsize=(20,10)) # 设置图形大小
# 顾客年龄分布直方图
plt.subplot(2,2,1)
sns.distplot(data['customer_age'], bins=30, kde=True, color='blue') # 绘制带核密度估计的直方图
plt.title('顾客年龄分布', fontsize=16) # 设置标题
plt.xlabel('顾客年龄', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 顾客年龄组别分布柱状图
plt.subplot(2,2,2)
order = ['18-25', '26-35', '36-45', '46-55', '56+'] # 自定义年龄组顺序
sns.countplot(data=data, x='age_group', color='orange', order=order) # 按指定顺序绘制柱状图
plt.title('顾客年龄组别分布', fontsize=16) # 设置标题
plt.xlabel('顾客年龄组别', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 顾客性别分布柱状图
plt.subplot(2,2,3)
sns.countplot(data=data, x='customer_gender', color='green') # 绘制性别分布柱状图
plt.title('顾客性别分布', fontsize=16) # 设置标题
plt.xlabel('顾客性别', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 顾客性别比例饼图
plt.subplot(2,2,4)
plt.pie(data['customer_gender'].value_counts(), # 绑制饼图
labels=data['customer_gender'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%', colors=['green', 'orange']) # 绘制饼图并显示百分比
plt.title('顾客性别比例', fontsize=16) # 设置标题
plt.axis('equal') # 确保饼图是圆形
plt.savefig("顾客信息.png") # 保存图形
#1、从顾客年龄组别的分布可以了解到,26-55岁间的顾客最多。
#2、从顾客性别的分布可以了解到,男性顾客比女性顾客多,男性占比60.4%,女性占比39.6%。
# 区域分布可视化
plt.figure(figsize=(20,5)) # 设置图形大小
sns.countplot(x='region', data=data, palette='Set2') # 绑制计数柱状图
plt.title('区域分布', fontsize=20) # 设置标题
plt.xlabel('区域', fontsize=16) # 设置x轴标签
plt.ylabel('数量', fontsize=16) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
plt.savefig("区域分布.png") # 保存图形
# 2.2 商品信息可视化
plt.figure(figsize=(20,5)) # 设置图形大小
# 产品名称分布柱状图
plt.subplot(1, 2, 1)
sns.countplot(x='product_name', data=data, palette='Set2') # 绘制产品名称分布柱状图
plt.xlabel('产品名称', fontsize=16) # 设置x轴标签
plt.ylabel('数量', fontsize=16) # 设置y轴标签
plt.title('产品分布', fontsize=20) # 设置标题
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 产品类型分布柱状图
plt.subplot(1, 2, 2)
sns.countplot(x='category', data=data, palette='Set2') # 绘制产品类型分布柱状图
plt.xlabel('产品类型', fontsize=16) # 设置x轴标签
plt.ylabel('数量', fontsize=16) # 设置y轴标签
plt.title('产品类型分布', fontsize=20) # 设置标题
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
plt.savefig("商品信息.png") # 保存图形
#总体而言,茶叶是销量最多的产品类型,远超其他产品类型。
#具体产品分布上,销量最多的产品是古田银耳,共1474单;其次是安溪铁观音,共1461单;第三名是宁德大黄鱼,共1438单。
# 销售相关指标分布可视化
plt.figure(figsize=(20,6)) # 设置图形大小
# 价格分布直方图
plt.subplot(1, 3, 1)
sns.distplot(data['price'], kde=True, color='blue') # 绘制价格分布直方图
plt.title('价格分布', fontsize=16) # 设置标题
plt.xlabel('价格', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 数量分布直方图
plt.subplot(1, 3, 2)
sns.distplot(data['quantity'], kde=True, color='green') # 绘制数量分布直方图
plt.title('数量分布', fontsize=16) # 设置标题
plt.xlabel('数量', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
# 销售额分布直方图
plt.subplot(1, 3, 3)
sns.distplot(data['sales_amount'], kde=True, color='orange') # 绘制销售额分布直方图
plt.title('销售额分布', fontsize=16) # 设置标题
plt.xlabel('销售额', fontsize=12) # 设置x轴标签
plt.ylabel('频率', fontsize=12) # 设置y轴标签
plt.savefig("销售.png") # 保存图形
# 2.3 平台及优惠信息可视化
plt.figure(figsize=(20,12)) # 设置图形大小
# 平台分布柱状图
plt.subplot(2, 3, 1)
sns.countplot(x='channel', data=data, palette='Set2') # 绘制平台分布柱状图
plt.title('平台分布', fontsize=16) # 设置标题
plt.xlabel('平台', fontsize=14) # 设置x轴标签
plt.ylabel('频数', fontsize=14) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 平台比例饼图
plt.subplot(2, 3 ,2)
plt.pie(data['channel'].value_counts(), # 绑制饼图
labels=data['channel'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%') # 绘制平台比例饼图
plt.title('平台比例', fontsize=16) # 设置标题
# 优惠情况分布柱状图
plt.subplot(2,3, 3)
sns.countplot(x='promotion', data=data, palette='Set2') # 绘制优惠情况分布柱状图
plt.title('优惠情况分布', fontsize=16) # 设置标题
plt.xlabel('优惠情况', fontsize=14) # 设置x轴标签
plt.ylabel('频数', fontsize=14) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 优惠情况比例饼图
plt.subplot(2, 3, 4)
plt.pie(data['promotion'].value_counts(), # 绑制饼图
labels=data['promotion'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%') # 绘制优惠情况比例饼图
plt.title('优惠情况比例', fontsize=16) # 设置标题
# 是否返单分布柱状图
plt.subplot(2, 3, 5)
sns.countplot(x='return_flag', data=data, palette='Set2') # 绘制是否返单分布柱状图
plt.title('是否返单分布', fontsize=16) # 设置标题
plt.xlabel('是否返单', fontsize=14) # 设置x轴标签
plt.ylabel('频数', fontsize=14) # 设置y轴标签
# 在每个柱子上方添加具体数值
for p in plt.gca().patches:
plt.gca().annotate(format(p.get_height(), '.0f'), # 获取当前坐标轴并在柱状图顶部标注数值
(p.get_x() + p.get_width() / 2., p.get_height()), # 设置标注位置为柱子顶部中心
# 设置标注文本的水平对齐、垂直偏移等显示参数
ha='center', va='center', xytext=(0, 5), textcoords='offset points')
# 是否返单比例饼图
plt.subplot(2, 3, 6)
plt.pie(data['return_flag'].value_counts(), # 绑制饼图
labels=data['return_flag'].value_counts().index, # 统计各个值的出现频次
autopct='%1.1f%%') # 绘制是否返单比例饼图
plt.title('是否返单比例', fontsize=16) # 设置标题
plt.savefig("平台及优惠信息.png") # 保存图形
#从平台的分布上可以了解到,淘宝订单数量是最多的,占比29.6%;京东平台订单数量第二,共2504单,占比25%;第三是拼多多平台;自由平台的订单数量最少。
#从优惠情况可以了解到,没有优惠的订单占比50.3%,有优惠的订单占比49.7%。其中,满减订单占比29.4%,折扣订单占比20.3%。
#是否返单的情况可以了解到,有返单的订单占比4.8%,没有返单的订单占比95.2%。数据预处理是分析的第一步,通过查看数据结构和基本统计信息来了解数据质量:
shape:返回数据维度(行数, 列数)info():显示每列的数据类型和非空值数量describe().T:转置描述统计表,更易阅读isnull().sum():统计每列缺失值数量import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 读取数据并输出基本信息
data = pd.read_csv('processed_data.csv')
print('数据形状:', data.shape)
print('\n数据前5行:')
print(data.head())
print('\n数据信息:')
print(data.info())
print('\n描述统计:')
print(data.describe().T)在初步检查中,需要关注三类数据类型:
| 数据类型 | 示例字段 | 说明 |
|---|---|---|
| 数值型 (int/float) | 年龄、金额、数量 | 可直接参与计算 |
| 字符型 (object) | 产品名称、类别 | 需要编码或分组分析 |
| 日期型 (datetime) | 下单日期 | 需要格式转换后提取特征 |
在 Matplotlib 中显示中文需要两步配置:
plt.rcParams['font.sans-serif'] = ['SimHei']:设置黑体为默认字体plt.rcParams['axes.unicode_minus'] = False:修复负号显示问题产品分类是电商数据分析的基础,农产品通常采用混合分类策略:
# 产品名称到分类的映射字典
category_mapping = {
'安溪铁观音': '茶叶',
'武夷岩茶': '茶叶',
'福州茉莉花': '茶叶',
'古田银耳': '食用菌',
'建宁莲子': '中药材',
'琯溪蜜柚': '水果',
'宁德大黄鱼': '水产品'
}
# map()根据字典映射,fillna()保留未匹配的值
data['category'] = data['product_name'].map(
category_mapping
).fillna(data['category'])
# 验证映射结果
print('分类映射结果:')
print(data[['product_name', 'category']].drop_duplicates())
print('\n分类统计:')
print(data['category'].value_counts())map() 函数的核心机制map(dict):根据字典进行键值映射fillna():处理未匹配的值(保留原分类)for 循环映射高效 100 倍以上drop_duplicates():去重查看唯一映射关系根据 Rubin (1976) 的分类,缺失值分为三种:
| 类型 | 含义 | 处理方法 |
|---|---|---|
| MCAR | 完全随机缺失,与任何变量无关 | 直接删除或插补 |
| MAR | 缺失与观测变量相关 | 多重插补 |
| MNAR | 缺失与未观测变量相关 | 建模机制 |
# 缺失值统计
missing_counts = data.isnull().sum()
missing_pct = (missing_counts / len(data)) * 100
missing_df = pd.DataFrame({
'缺失数': missing_counts,
'缺失比例': missing_pct
})
print(missing_df)
# 缺失值可视化
plt.figure(figsize=(10, 6))
missing_df['缺失比例'].plot(kind='bar', color='steelblue', alpha=0.7)
plt.title('各字段缺失值比例', fontsize=14)
plt.xlabel('字段名', fontsize=12)
plt.ylabel('缺失比例(%)', fontsize=12)
plt.xticks(rotation=45)
plt.axhline(y=5, color='red', linestyle='--', label='5%阈值')
plt.legend()
plt.tight_layout()
plt.show()异常值 (Outlier) 指显著偏离其他观测值的数据点:
| 方法类别 | 具体方法 | 适用场景 |
|---|---|---|
| 基于统计量 | 3σ 原则、IQR 方法 | 正态/近似正态分布 |
| 基于可视化 | 箱线图、散点图 | 快速直观判断 |
| 基于模型 | DBSCAN、孤立森林 | 高维复杂数据 |
IQR 方法的判定规则:
超出上下界的数据点即为异常值。
# 选择数值型字段
numeric_cols = data_clean.select_dtypes(include=[np.number]).columns
# 绘制箱线图
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes = axes.flatten()
for i, col in enumerate(numeric_cols[:4]):
data_clean.boxplot(column=col, ax=axes[i])
axes[i].set_title(f'{col} - 箱线图', fontsize=12)
axes[i].grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
# IQR 异常值检测函数
def detect_outliers_iqr(series):
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
return ((series < lower) | (series > upper)).sum()
# 输出异常值统计
print('\n异常值统计(IQR方法):')
for col in numeric_cols:
outlier_count = detect_outliers_iqr(data_clean[col].dropna())
print(f'{col}: {outlier_count}个异常值')| 维度 | 指标 | 含义 |
|---|---|---|
| 集中趋势 | 均值、中位数、众数 | 数据的中心位置 |
| 离散程度 | 方差、标准差、极差 | 数据偏离中心的程度 |
| 分布形状 | 偏度、峰度 | 对称性和尖锐程度 |
from scipy.stats import skew, kurtosis
sales = data_clean['sales_amount']
# 绘制分布图
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 直方图(含均值和中位数参考线)
axes[0].hist(sales, bins=50, color='steelblue', alpha=0.7,
edgecolor='black')
axes[0].axvline(sales.mean(), color='red', linestyle='--',
linewidth=2, label=f'均值={sales.mean():.2f}')
axes[0].axvline(sales.median(), color='green', linestyle='--',
linewidth=2, label=f'中位数={sales.median():.2f}')
axes[0].set_title('销售额分布直方图', fontsize=14)
axes[0].set_xlabel('销售额(元)', fontsize=12)
axes[0].set_ylabel('频数', fontsize=12)
axes[0].legend(fontsize=10)
# 箱线图
axes[1].boxplot(sales, vert=True)
axes[1].set_title('销售额箱线图', fontsize=14)
axes[1].set_ylabel('销售额(元)', fontsize=12)
plt.tight_layout()
plt.show()
# 输出分布统计量
print(f'均值: {sales.mean():.2f}')
print(f'中位数: {sales.median():.2f}')
print(f'标准差: {sales.std():.2f}')
print(f'偏度: {skew(sales):.4f}')
print(f'峰度: {kurtosis(sales):.4f}')# 按品类聚合统计
category_sales = data_clean.groupby('category')['sales_amount'].agg([
'sum', 'count', 'mean'
])
category_sales.columns = ['总销售额', '订单数', '平均单价']
category_sales = category_sales.sort_values('总销售额', ascending=False)
print('\n品类销售统计:')
print(category_sales)
# 可视化
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 总销售额柱状图
category_sales['总销售额'].plot(kind='bar', ax=axes[0],
color='steelblue', alpha=0.7)
axes[0].set_title('各品类总销售额', fontsize=14)
axes[0].set_xlabel('产品类别', fontsize=12)
axes[0].set_ylabel('销售额(元)', fontsize=12)
axes[0].tick_params(axis='x', rotation=45)
# 订单数饼图
axes[1].pie(category_sales['订单数'],
labels=category_sales.index,
autopct='%1.1f%%', startangle=90,
colors=sns.color_palette('Set3'))
axes[1].set_title('各品类订单数占比', fontsize=14)
plt.tight_layout()
plt.show()根据可视化结果,可以识别出:
| 特征 | 说明 | 示例 |
|---|---|---|
| 趋势性 | 长期上升或下降趋势 | 年销售额逐年增长 |
| 季节性 | 固定周期的波动 | 春节前后销量高峰 |
| 周期性 | 不固定周期的波动 | 经济周期影响消费 |
| 随机性 | 不可预测的噪声 | 突发事件造成波动 |
# 确保日期格式正确
data_clean['下单日期'] = pd.to_datetime(data_clean['order_date'])
# 提取时间特征
data_clean['年'] = data_clean['下单日期'].dt.year
data_clean['月'] = data_clean['下单日期'].dt.month
data_clean['日'] = data_clean['下单日期'].dt.day
data_clean['星期'] = data_clean['下单日期'].dt.dayofweek
data_clean['小时'] = data_clean['下单日期'].dt.hour
# 按月统计
monthly_sales = data_clean.groupby('月')['sales_amount'].agg([
'sum', 'count'
])
monthly_sales.columns = ['月销售额', '月订单数']
print('月度销售统计:')
print(monthly_sales)fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 月销售额折线图
monthly_sales['月销售额'].plot(ax=axes[0], marker='o', linewidth=2)
axes[0].set_title('月销售额趋势', fontsize=14)
axes[0].set_xlabel('月份', fontsize=12)
axes[0].set_ylabel('销售额(元)', fontsize=12)
axes[0].grid(True, alpha=0.3)
# 月订单数柱状图
monthly_sales['月订单数'].plot(kind='bar', ax=axes[1],
color='coral', alpha=0.7)
axes[1].set_title('月订单数分布', fontsize=14)
axes[1].set_xlabel('月份', fontsize=12)
axes[1].set_ylabel('订单数', fontsize=12)
plt.tight_layout()
plt.show()分析工作日与周末的消费差异,为营销和物流策略提供依据:
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 平均销售额对比
weekend_comparison['mean'].plot(kind='bar', ax=axes[0],
color=['skyblue', 'orange'], alpha=0.7)
axes[0].set_title('平均销售额对比', fontsize=14)
axes[0].set_ylabel('平均销售额(元)', fontsize=12)
axes[0].tick_params(axis='x', rotation=0)
# 总销售额对比
weekend_comparison['sum'].plot(kind='bar', ax=axes[1],
color=['skyblue', 'orange'], alpha=0.7)
axes[1].set_title('总销售额对比', fontsize=14)
axes[1].set_ylabel('总销售额(元)', fontsize=12)
axes[1].tick_params(axis='x', rotation=0)
plt.tight_layout()
plt.show()价格弹性 (Price Elasticity) 衡量需求量对价格变化的敏感度:
\[E_p = \frac{\%\Delta Q}{\%\Delta P} = \frac{\partial Q}{\partial P} \times \frac{P}{Q}\]
# 按产品统计价格与销量
product_stats = data_clean.groupby('product_name').agg({
'sales_amount': 'sum',
'quantity': 'sum',
'unit_price': 'mean'
})
product_stats['单价'] = (product_stats['sales_amount']
/ product_stats['quantity'])
# 计算相关系数
corr_coef = product_stats[['单价', 'quantity']].corr().iloc[0, 1]
print(f'价格与销量的相关系数: {corr_coef:.4f}')fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 散点图
axes[0].scatter(product_stats['单价'],
product_stats['quantity'], s=100, alpha=0.6)
axes[0].set_title(f'价格-销量散点图 (r={corr_coef:.4f})', fontsize=14)
axes[0].set_xlabel('单价(元)', fontsize=12)
axes[0].set_ylabel('销量', fontsize=12)
# 添加产品标签
for idx, row in product_stats.iterrows():
axes[0].annotate(idx, (row['单价'], row['quantity']), fontsize=8)
# 热力图
sns.heatmap(product_stats[['单价', 'quantity']].corr(),
annot=True, cmap='coolwarm', center=0, ax=axes[1],
cbar_kws={'label': '相关系数'})
axes[1].set_title('相关性热力图', fontsize=14)
plt.tight_layout()
plt.show()产品共现分析可以发现哪些产品经常被一起购买:
from itertools import combinations
# 获取每个订单的产品列表
order_products = data_clean.groupby('order_id')[
'product_name'
].apply(list)
# 统计产品对共现次数
co_occurrence = {}
for products in order_products:
for combo in combinations(sorted(products), 2):
co_occurrence[combo] = co_occurrence.get(combo, 0) + 1
# 转换为 DataFrame 并排序
co_df = pd.DataFrame(list(co_occurrence.items()),
columns=['产品对', '共现次数'])
co_df = co_df.sort_values('共现次数', ascending=False).head(10)
print('Top 10产品组合:')
print(co_df)plt.figure(figsize=(12, 6))
plt.barh(range(len(co_df)), co_df['共现次数'],
color='steelblue', alpha=0.7)
plt.yticks(range(len(co_df)),
[f'{p1} + {p2}' for p1, p2 in co_df['产品对']])
plt.xlabel('共现次数', fontsize=12)
plt.title('产品共现分析(Top 10)', fontsize=14)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.show()| 分析环节 | 核心方法 | 关键工具 |
|---|---|---|
| 数据准备 | 分类映射、类型转换 | map(), pd.to_datetime() |
| 质量检查 | 缺失值、异常值检测 | isnull(), IQR 方法 |
| 分布分析 | 偏度、峰度、直方图 | scipy.stats, hist() |
| 时间分析 | 月度趋势、周末效应 | dt 属性、groupby() |
| 多变量 | 相关分析、共现分析 | corr(), combinations |
[商业大数据分析与应用]